use axum::{extract::DefaultBodyLimit, Router};
use serde::Deserialize;
use tower_http::services::{ServeDir, ServeFile};

use super::cors;
type SevrDir = ServeDir<tower_http::set_status::SetStatus<ServeFile>>;

fn default_web_addr() -> String {
    "0.0.0.0:3000".to_owned()
}

static DEFAULT_BODY_LIMIT: usize = 5 * 1024 * 1024;

#[derive(Clone, Deserialize, Debug)]
#[allow(dead_code)]
pub struct WebConfig {
    /// 默认 0.0.0.0:3000
    #[serde(default = "default_web_addr")]
    pub addr: String,
    #[serde(default)]
    sso_mode: bool,
    #[serde(default)]
    body_limit: Option<usize>,
    #[serde(default)]
    root: String,
    /// localpath(:server_path)
    #[serde(default)]
    statics: Vec<String>,
    #[serde(default)]
    proxy: Vec<String>,
    #[serde(default)]
    not_found_page: Option<String>,
    #[serde(default)]
    cors: Option<cors::CorsConfig>,
}

impl WebConfig {
    pub fn root(&self) -> &str {
        self.root.as_str()
    }

    // html的静态文件服务
    pub fn html_server(&self) -> SevrDir {
        let srv = ServeDir::new(self.root());
        let not_found = self._not_found_page(self.sso_mode);
        srv.not_found_service(ServeFile::new(not_found))
    }

    fn _not_found_page(&self, sse_mode: bool) -> String {
        let index = if sse_mode {
            "index.html".to_owned()
        } else {
            match &self.not_found_page {
                Some(p) => p.clone(),
                _ => "404.html".to_owned(),
            }
        };
        if index.starts_with("/") {
            index
        } else {
            if self.root.is_empty() {
                index
            } else {
                format!("{}/{}", self.root.trim_end_matches('/'), index)
            }
        }
    }

    /// 分割:,前半是文件目录,后半是url路径
    fn static_server_from_str(&self, stc: &str) -> (String, SevrDir) {
        let s: Vec<&str> = stc.split(":").collect();
        if s.is_empty() || s.len() > 2 {
            panic!("error web config of statics:\"{}\"", stc);
        };
        let root = unsafe { s.get_unchecked(0) };
        let prefix: &&str = s.get(1).unwrap_or(root);
        (
            prefix.to_string(),
            ServeDir::new(root)
                .append_index_html_on_directories(false)
                .not_found_service(ServeFile::new(self._not_found_page(false))),
        )
    }

    pub fn get_route<S>(&self) -> Router<S>
    where
        S: Send + Sync + Clone + 'static,
    {
        // let mut router = Router::new().nest_service("/", self.html_server());
        // for stc in &self.statics {
        //     let (path, src) = self.static_server_from_str(stc);
        //     router = router.nest_service(&format!("/{}", path.trim_start_matches('/')), src);
        // }
        Router::new()
    }

    pub fn get_route_with<S1, S2, F>(&self, wrap: F) -> Router<S2>
    where
        F: FnOnce(Router<S1>) -> Router<S2>,
        S1: Send + Sync + Clone + 'static,
        S2: Send + Sync + Clone + 'static,
    {
        self.apply_layer_to(wrap(self.get_route()))
    }

    /// static目录的路由
    /// 对应配置文件的statics数组
    /// 分割:,前半是文件目录,后半是url路径
    pub fn get_static_route<S>(&self) -> Router<S>
    where
        S: Send + Sync + Clone + 'static,
    {
        let mut router = Router::new();
        for stc in &self.statics {
            let (path, src) = self.static_server_from_str(stc);
            router = router.nest_service(&format!("/{}", path.trim_start_matches('/')), src);
        }
        router
    }

    /// 请求体限制的layer
    pub fn body_limit_layer(&self) -> DefaultBodyLimit {
        DefaultBodyLimit::max(self.body_limit.unwrap_or(DEFAULT_BODY_LIMIT))
    }

    pub fn apply_layer_to<S>(&self, router: Router<S>) -> Router<S>
    where
        S: Send + Sync + Clone + 'static,
    {
        let router: Router<S> = router.nest_service("/", self.html_server());
        let router = router.nest("", self.get_static_route());
        if let Some(cs) = &self.cors {
            router.layer(cs.create_cors_layer())
        } else {
            router
        }
        .layer(self.body_limit_layer())
    }

    pub fn cors(&self) -> Option<tower_http::cors::CorsLayer> {
        self.cors.as_ref().map(|cors| cors.create_cors_layer())
    }
}

impl Default for WebConfig {
    fn default() -> Self {
        Self {
            addr: default_web_addr(),
            sso_mode: true,
            not_found_page: None,
            root: "html".to_owned(),
            cors: Default::default(),
            statics: vec![],
            proxy: vec![],
            body_limit: None,
        }
    }
}
